/***************************************************************************
*   Copyright (C) 2005 by Alexander Nemish                              *
*   atlanter@gmail.com                                                  *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/
#include <kdebug.h>
#include <qfontmetrics.h>
#include <qregexp.h>
#include <qpainter.h>
#include <vector>
#include <cmath>
#include <iostream>

#include "renderer.h"

const QChar START_PARAGRAPH(1);
const QChar END_PARAGRAPH(2);
const int paraOffset = 30;

Renderer::Renderer():
		m_pageCount(0),
		m_linesPerPage(0),
		m_empty(true)
{}


Renderer::~Renderer()
{
	TParagsIter end = m_parags.end();
	for (TParagsIter it = m_parags.begin(); it != end; ++it)
		delete (*it);
}

void Renderer::load(const QStringList & a_list)
{
	m_empty = true;
	
	if (a_list.isEmpty())
		return;
	
	m_text = a_list;
	m_empty = false;
	
	typedef QStringList::iterator TStringIter;
	TStringIter end = m_text.end();
	
	for (TStringIter it = m_text.begin(); it != end; ++it)
		(*it).replace(QChar('\t'), " ");
	
	render();
}

void Renderer::render()
{
	if (m_empty) 
		return;
	
	m_lines.clear();
	QFontMetrics fm(font());
	QString string;
	TStringIter it(m_text.constBegin());

	while (it != m_text.constEnd())
	{
		size_t i = 0;
		//skip spaces;
		while ((*it)[i] == ' ') i++;
		string = (*it).mid(i);
		//process string until paragraph ends
		while (++it != m_text.constEnd())
		{
			QChar const ch((*it)[0]);
			//insert empty lines
			if ((*it).isEmpty())
				break;
			//check whether paragraph not ends
			if ((ch != ' ') && (ch != '-'))
			{
				string += " " + *it;
			}
			else break;
		}
		parseString(string);
		//emit loaded((int) curLine/lineCount );
	}

	m_linesPerPage = m_pageSize.height() / (QFontMetrics(font() ).height());
	m_pageCount = m_linesPerPage ? (m_lines.count() / m_linesPerPage) : 0;
	kdDebug() << "done" << endl;
}

void Renderer::parseString(const QString & a_string)
{
// 	QString string(a_string.mid(1, a_string.length() - 1));
	QString string(a_string);
	QFontMetrics fm(font());
	const int avgCharWidth = fm.width("j");
	//calc approx string width
	const unsigned int avgLen = m_pageSize.width() / avgCharWidth;
	unsigned int len;
	m_isStartAdded = false;
	int curWidth = width(string);
	
	if (curWidth <=m_pageSize.width())
	{
		m_isStartAdded = true;
		addLine(START_PARAGRAPH + string + END_PARAGRAPH);
		return;
	}
	
	for (; curWidth > m_pageSize.width(); curWidth = width(string))
	{
		len = avgLen;
		//turn left if missed
		while ((width(string.left(len)) > m_pageSize.width()) && (len > 0))
			--len;
		//turn right if missed
		while ((width(string.left(len)) <= m_pageSize.width()) 
				&& (len < string.length()))
			++len;
		--len;
		//check whether we in a word
		if ((string[len - 1] != ' ') && (string[len] != ' '))
		{
			//find last word
			int i = wordAt(string, len);
			//check whether its width less than page width
			if (fm.width(getWord(string, i)) <= m_pageSize.width())
			{
				//if so, move last word to the next line
				QString jstring = string.left(i);
				addLine(jstring.stripWhiteSpace());
				string = string.right(string.length() - i);
			}
			else
			{
				//if its width greater than page width - split it
				addLine(string.left(len));
				string = string.right(string.length() - len);
			}
		}
		else if (string[len - 1] == ' ' && string[len] == ' ')
		{
			int idx = len - 1;
			while (string[--idx] == ' ');
			addLine(string.left(idx + 1));
			idx = len;
			while (string[++idx] == ' ');
			string = string.right(string.length() - idx);
		}
		else if (string[len - 1] == ' ')
		{
			int idx = len - 1;
			while (string[--idx] == ' ');
			addLine(string.left(idx + 1));
// 			std::cout << "Res: = '" << string.left(idx+1).local8Bit() << "'" << std::endl;
			string = string.right(string.length() - len);
		}
		else if (string[len] == ' ')
		{
			int idx = len;
			while (string[++idx] == ' ');
			addLine(string.left(len - 0));
			string = string.right(string.length() - idx);			
		}
	}
	//last line in multiline para.
	addLine(string + END_PARAGRAPH);
}

QString Renderer::getWord(const QString & a_string, int a_idx)
{
	int idx = a_idx;
	while (a_string[++idx] != ' ' && idx < a_string.length());
	return QString(a_string.mid(a_idx, idx - a_idx));
}

int Renderer::wordAt(const QString & string, int len)
{
	while (--len >= 0)
		if (string[len] == ' ')
			return ++len;
	return 0;
}

void Renderer::setFont(const QFont & font)
{
	m_font = font;
	render();
}

void Renderer::setPageSize(const QSize & size)
{
	m_pageSize = size;
}

void Renderer::drawPage(QPainter & paint, QRect rect, int pageNumber)
{
	QFontMetrics fm(font());
	int height = fm.height();
	int line = 1;
	size_t count = m_lines.count();
	
	if (count == 0) return;
	if (pageNumber * m_linesPerPage >= count) return;
	
	typedef QStringList::const_iterator SI;
	SI end = m_lines.constEnd();
	SI it = m_lines.at(pageNumber * m_linesPerPage);
	for (; it != end && (line <= m_linesPerPage); ++line, ++it)
		drawLine(paint, rect.left(), rect.top() + line * height, *it);
}

void Renderer::drawLine(QPainter & paint, int x, int y, const QString & string)
{
	//TODO: Optimize this stuff
	const int length = string.length();
	const bool isStartParag = (string[0] == START_PARAGRAPH);
	
	if (string[length - 1] == ' ')
	{
		std::cout << "Fuck: " << string.local8Bit() << std::endl;
		return;
	}
	
	if (length == 0) return;
	
	if (isStartParag && string[length - 1] == END_PARAGRAPH)
	{
		if (length < 3) return;
		paint.drawText( x + paraOffset, y, string.mid(1, length - 2) );
		return;
	}

	if (string[length - 1] == END_PARAGRAPH)
	{
		paint.drawText(x, y, string.left(length - 1));
		return;
	}

	QFontMetrics fm(font());
	static int startPageCharWidth = fm.width(START_PARAGRAPH);
	int pageWidth = m_pageSize.width();
	int spaceWidth = fm.width(" ");
	int width = fm.width(string);
	int stringOffset = 0;
	
	if (isStartParag)
	{
		width += 30 - startPageCharWidth; 
		stringOffset = 1;
	}
	
	const int curWidth = width;
	
	if (curWidth < pageWidth)
	{
		long pos = 0, off = 0;
		int spaceCount = 0;
		//count spaces
		
		if (isStartParag)
			x += paraOffset;
		
		while (((pos = string.find(' ', off)) != -1) && (pos != length - 1))
		{
			off = pos + 1;
			++spaceCount;
		}
		if (spaceCount)
		{
			double x1 = x;
			const double avgLen = ((double)(pageWidth - curWidth) / spaceCount);
			//ignore START_PARAGRAPH char
			int idx = (isStartParag) ? 1 : 0;
			pos = off = 0;

			QString tmp;
			while (((pos = string.find(' ', off)) != -1))
			{
				if (idx < pos)
				{
					tmp = string.mid(idx, pos - idx);
					paint.drawText(std::ceil(x1), y, tmp);
					x1 += fm.width(tmp);
				}
				x1 += spaceWidth + avgLen;
				idx = off = pos + 1;
			}
			//last chunk
			paint.drawText(std::ceil(x1), y, string.right(length - idx));
		}
		else
		{
			paint.drawText(x, y, string.right(length - stringOffset));
		}
	}
	else
	{
		paint.drawText(x, y, string.right(length - stringOffset));
	}
}

Paragraph::Paragraph()
	:m_width(0)
{
}

Paragraph::Paragraph(const QString & a_str)
	:m_text(a_str), m_width(0)
{
}

Paragraph::Paragraph( const QString & a_str, long a_width )
	:m_text(a_str), m_width(a_width)
{
}

void Renderer::addLine(const QString & a_string)
{
	if (m_isStartAdded)
		m_lines << a_string;
	else
	{
		m_lines << (START_PARAGRAPH + a_string);
		m_isStartAdded = true;
	}
}

int Renderer::width(const QString & a_string) const
{
	QFontMetrics fm(font());
	int w = fm.width(a_string);
	return m_isStartAdded ? w : w + paraOffset;
}

#include "renderer.moc"
